<template>
  <div class="qhh-virtual-tree">
    <section class="search-bar" v-if="hasInput && !$scopedSlots.customSearch">
      <slot name="preInput"></slot>
      <div class="input">
        <label>
          <input
            type="text"
            class="filter-input"
            :placeholder="placeholder"
            v-model="keyword"
            @keyup.13="search_"
            @input="debounceInput"
          />
        </label>
        <i
          class="clear-input"
          v-if="keyword"
          @click="reset_"
        ></i>
      </div>
      <button class="search-btn" @click="search_">搜索</button>
    </section>
    <slot name="customSearch" :search="customSearchEvent" :reset="reset_"></slot>
    <section ref="content-wrap" class="content-wrap" :style="{overflowX: overflowX ? 'hidden' : 'auto'}" @scroll="onScroll_">
      <div class="tree-phantom" :style="`height: ${phantomHeight}px`"></div>
      <div class="tree-content" v-if="!isLoading" :style="`transform: translateY(${startIndex * itemHeight}px)`">
        <template v-for="item in renderList">
          <section
            v-if="item.path"
            :key="item[c_options.idKey]"
            class="item-section"
            :style="{height: itemHeight + 'px'}"
            :class="[{ 'is-hidden': item.isHidden }, itemClass]"
            @contextmenu.prevent="rightClick_($event, item)"
          >
            <div class="item" :style="`padding-left: ${(item.path.length - 1) * Number(indent)}px`">
              <div>
                <div
                    class="expand-icon-box"
                    :style="{visibility: item.isLeaf ? 'hidden' : 'visible'}"
                    @click="onExpand_(item)"
                >
                  <div v-if="$slots.expandIcon" class="custom-expand-node" :style="{transform: 'rotate(' + (item.isExpand ? expandRotate : 0) + 'deg)'}">
                    <slot name="expandIcon" :expand="item.isExpand" />
                  </div>
                  <div v-else class="expand-node" :style="{width: c_checkboxOptions.checkBoxSize,height: c_checkboxOptions.checkBoxSize, transform: 'rotate(' + (item.isExpand ? expandRotate : 0) + 'deg)'}" :class="{'expand-node-border': !expandIcon}">
                    <div class="expand-icon" :style="`background-image: url('${item.isExpand ? foldIcon : (expandIcon ? expandIcon : defaultExpandIcon)}')`"></div>
                  </div>
                </div>
                <dt-checkbox
                    v-model="item.checked"
                    :preCheckbox="c_checkboxOptions.preCheckbox"
                    :checkBoxSize="c_checkboxOptions.checkBoxSize"
                    :options="c_options"
                    :checkedIcon="c_checkboxOptions.checkedIcon"
                    :checkedDisabledIcon="c_checkboxOptions.checkedDisabledIcon"
                    :checkedPartIcon="c_checkboxOptions.checkedPartIcon"
                    :checkedPartDisabledIcon="c_checkboxOptions.checkedPartDisabledIcon"
                    :indeterminate="item.indeterminate"
                    :disabled="item[c_options.disabledKey]"
                    :isLeaf="item.isLeaf"
                    :showCheckboxLeafOnly="c_checkboxOptions.showCheckboxLeafOnly"
                    :showCheckboxByKeys="c_checkboxOptions.showCheckboxByKeys"
                    :checkedAction="checkedAction"
                    :showCheckbox="c_checkboxOptions.showCheckbox"
                    :checkStrictly="checkStrictly"
                    :node="item"
                    :label-class="labelClass"
                    :class="{ 'is-disabled': item[c_options.disabledKey] }"
                    @on-checked="onChecked_(item)"
                    @on-click-label="onClickLabel_(item)"
                    @on-click-item="onClickItem_(item)"
                >
                  <slot :item="item">{{ item[c_options.labelKey] }}</slot>
                  <i v-if="(!item.isLeaf || !item.hasOwnProperty(c_countOptions.countKey)) && c_countOptions.showCount" class="count">({{ item.leafCount_ }})</i>
                </dt-checkbox>
              </div>

            </div>
          </section>
        </template>
      </div>
    </section>
    <section v-if="renderList.length <= 0 || isLoading" class="no-data">
      <p v-if="isLoading"><slot name="loading">加载中...</slot></p>
      <p v-else>{{ emptyText }}</p>
    </section>
  </div>
</template>
<script>
import Checkbox from './checkbox.vue';
import defaultExpandIcon from '../../assets/plus.svg'
import defaultFoldIcon from '../../assets/indeterminate.svg'

import { throttle, debounce, clearAll } from '../../utils';
import {
  depthFirstEach,
  listToTree,
} from './util.js';
import {defaultCheckboxOptions, defaultCountOptions, defaultOptions} from "./options";

import {checkedOnlyFilter, extentFilter, getPid, keywordFilter} from "./util";

class BigData {
  _data = []; // 海量数据 tree
  list = []; // 扁平化的tree
  listMap = {}; // this.big.list 对应的 map
  filterList = []; // 根据关键词过滤后的list
  filterMap = {}; // 根据关键词过滤后的list 对应的map
  filterTree = []; // 根据关键词过滤后的tree
  disabledList = []; // disabled 为true组成的数组
  checkedKeys = []; // 选中的 ids
  checkedNodes = []; // 选中的 nodes
  indeterminateNodes = []; // 半选中的 nodes
  indeterminateKeys = []; // 半选中的 nodes
  allCheckedList = []; // 所有选中的节点， 用于开启开关 isOnlyInCheckedSearch 时， 仅在选中节点里筛选。
}

export default {
  name: 'VirtualTree',
  model: {
    prop: 'searchKeyword',
    event: 'update'
  },
  components: {
    'dt-checkbox': Checkbox,
  },
  props: {
    // 每一项的高度 必须
    itemHeight: {
      type: Number,
      required: true
    },
    labelClass: {
      type: String,
      default: ""
    },
    itemClass: {
      type: String,
      default: ""
    },
    overflowX: {type:Boolean, default: true},
    // 展开图标旋转的角度
    expandRotate: {type: Number, default: 180},
    checkboxOptions: {
      type: Object,
      default: () => (defaultCheckboxOptions)
    },
    // 替换expand图标
    expandIcon: {type: [String, null], default: null },
    // 替换折叠图标
    foldIcon: {type: String, default: defaultFoldIcon },

    // 树参数
    options: { type: Object, default: () => (defaultOptions)},
    // 搜索关键字
    searchKeyword: { type: String, default: ''},
    // 含有过滤输入框
    hasInput: { type: Boolean, default: false },
    // 缩进
    indent: { type: [String, Number], default: 15 },
    // 指定ids展开
    expandKeys: { type: Array, default: () => [] },
    // 展开 level， all: 展开全部； 1: 只展示第一层(最外层)；2: 展示到第二层；、、、
    expandLevel: { type: [String, Number], default: 'all' },
    // 输入框 placeholder
    placeholder: { type: String, default: '请输入关键字进行查找,支持逗号分隔多匹配' },
    // isLoading
    isLoading: { type: Boolean, default: false },
    // 在 label 上选中动作， 点击label选中 --> none: 不选中；click: 单击； dblclick: 双击；
    checkedAction: { type: String, default: 'none' },
    // 内容为空展示的文本
    emptyText: { type: String, default: '暂无数据' },
    // 默认勾选值
    defaultCheckedKeys: { type: Array, default: () => [] },
    // 父子不互相关联
    checkStrictly: { type: Boolean, default: false },
    // 扩展的过滤条件
    // 以[{key1: value1, key2:value2 ...}]形式传入
    // 会在过滤条件中根据
    extendFilter: {type: [Array, null]},
    countOptions: {
      type: Object, default: () => (defaultCountOptions)
    },
    // 缓存的数量
    cacheCount: {
      type: Number,
      default: 50
    },

    // 滚动渲染的throttle
    throttleScrollTime: {
      type: Number,
      default: 80
    }
  },
  emits: ['onClickCheckbox', 'onClickLabel', 'onChange', 'searchKeyword'],
  data() {
    this.big = null;
    return {
      keyword: '',
      defaultExpandIcon,
      count: 1, // 用于视图更新
      keywordLocal: '', // 关键词
      startIndex: 0, // 渲染的开始区间
      endIndex: 70, // 渲染的结束区间
      throttleScroll: '', // 节流
      debounceInput: '',
      isOnlyInCheckedSearch: false,
    };
  },
  computed: {
    /**
     * 计算得出options
     * */
    c_options() {
      return Object.assign({}, defaultOptions, this.options)
    },
    /**
     * 计算得出countOptions
     * */
    c_countOptions() {
      return Object.assign({}, defaultCountOptions, this.countOptions)
    },
    c_checkboxOptions() {
      return Object.assign({}, defaultCheckboxOptions, this.checkboxOptions)
    },
    // 过滤掉 hidden 节点
    unHiddenList() {
      // 此处需要count 来监测 ,big不在data中直接return this.big.filterList.filter(i => !i.isHidden)无法做到计算
      return this.count ? this.big.filterList.filter(i => !i.isHidden) : [];
    },
    // 虚拟高度，与隐藏的数量有关
    phantomHeight() {
      return this.unHiddenList.length * this.itemHeight;
    },
    /**
     * 实际渲染的列表
     * */
    renderList() {
      return this.unHiddenList.slice(this.startIndex, this.endIndex);
    },
  },
  watch: {
    // 默认选中的keys
    defaultCheckedKeys(newVal, oldVal) {
      if (newVal !== oldVal) {
        this.setCheckedKeys(newVal);
      }
    },
    // 展开的keys
    expandKeys(newVal, oldVal) {
      if (newVal !== oldVal) {
        this.setExpand(newVal);
      }
    },
    // 额外扩展的filter
    extendFilter() {
      const selectKeys = this.getCheckedNodes().filter(item => item.isLeaf).map(item => item[this.c_options.idKey])
      this.init_('notSetCheckedKey')
      this.setCheckedKeys(selectKeys)
    }
  },
  created() {
    this.big = new BigData();
    this.big.checkedKeys = JSON.parse(JSON.stringify(this.defaultCheckedKeys));
    const time = this.throttleScrollTime
    this.throttleScroll = throttle(this.setRenderRange_, time);
    this.debounceInput = debounce(this.init_, 300);
  },

  beforeDestroy() {
    this.clear();
  },

  methods: {
    // refreshExtendFilter() {
    //   // this.extendFilter = extendFilter
    //   this.init_('notSetCheckedKey')
    // },
    /**
     * 设置数据
     * @param {Array} data 树形数据
     * @param {boolean} clear 是否清除选中等状态
     * */
    setData(data, clear = true) {
      if (clear) {
        this.clear();
      }
      this.big._data = data;
      this.init_('init');
    },
    /**
     * 获取所有数据
     * */
    getAllData() {
      return this.big
    },

    /**
     * 初始化数据
     * */
    init_(op) {
      // op: init, restore, showCheckedOnly
      if (this.big._data.length === 0) return;
      if (op === 'init') {
        this.flatTree_(this.big._data);
        this.big.list.forEach(node => (this.big.listMap[node[this.c_options.idKey]] = node))

        // 如果不需要过滤数据的,则数量在此处计算(需要过滤的则在过滤中计算 , 此处只计算一次 ,过滤时 每次都会计算)
        if (!this.c_countOptions.filter) {
          this.computedCount_()
        }
        const count = this.getCount()
        this.$emit('onInit', { count })
      }
      this.initFilter_(op);
      if (op === 'init' || op === 'restore') {
        // 设置展开项
        this.initExpand_()
      }
      if (op !== 'notSetCheckedKey') {
        this.setCheckedKeys(this.big.checkedKeys);
      }
      this.backToTop();
    },
    /**
     * 获取各种数量
     * */
    getCount() {
      const count = {
        total: 0
      }
      if(this.big._data) {
        const extend = this.c_countOptions.extendCount
        this.big._data.forEach(item => {
          count.total += item.leafCount_
          extend && extend.forEach(i => {
            if (count[i.key + 'Count_'] == null) {
              count[i.key + 'Count_'] = 0
            }
            count[i.key + 'Count_'] += item[i.key + 'Count_']
          })
        })
      }
      return count
    },
    /**
     * 重置
     * @param {boolean} init 是否重置搜索
     * */
    reset_(init = true) {
      this.keyword = '';
      init && this.search_();
    },
    search_() {
      const selectedLeaf = this.getCheckedNodes().filter(item => item.isLeaf).map(item => item[this.c_options.idKey])
      this.init_('notSetCheckedKey')
      this.$nextTick()
      this.setCheckedKeys(selectedLeaf)
    },
    /***
     * 自定义搜索事件
     * */
    customSearchEvent(e) {
      this.keyword = e
      this.search_()
    },
    /**
     * 拉平tree
     * @param {Array} data 树形
     * */
    flatTree_(data) {
      this.big.list = []
      depthFirstEach({ tree: data, init: true, options: this.c_options }, node => {
        this.big.list.push(node);
      });
    },
    /***
     * item的click事件
     * */
    onClickItem_(node) {
      this.$emit('onClickItem', node)
    },
    /***
     * label的click事件
     * */
    onClickLabel_(node) {
      this.$emit('onClickLabel', node)
    },
    /***
     * 初始化处理展开逻辑
     * */
    initExpand_() {
      if (!this.big || this.big._data.length === 0) return;
      if (this.expandKeys.length > 0) {
        this.setExpand(this.expandKeys);
        return;
      }
      if (/^\d+$/.test(this.expandLevel)) {
        this.big.filterList.forEach(node => {
          node.isExpand = Boolean(node.path.length < this.expandLevel);
          node.isHidden = Boolean(node.path.length > this.expandLevel);
          this.initNode_(node);
        });
      } else {
        // 展开全部
        this.big.filterList.forEach(node => {
          node.isExpand = true;
          node.isHidden = false;
          this.initNode_(node);
        });
      }
      this.setCount_();
    },

    /***
     * 指定id展开
     * @param {string[]} keys id的列表
     * */
    setExpand(keys = []) {
      if (keys.length <= 0) return;
      if (!this.big || this.big._data.length === 0) return;
      const nodes = keys.map(id => this.big.listMap[id]).filter(v => v);
      const ids = Array.from(new Set(nodes.map(node => node.path).flat(1)));
      this.big.filterList.forEach(node => {
        if (node.isLeaf) {
          node.isExpand = false;
          // debugger
          const length = node.path.length
          const parentId = length > 1 ? node.path[length - 2] : null
          // 父级id 不存在 或者父级id不再需要显示ids中 , 则返回true
          node.isHidden = Boolean(!parentId || !ids.includes(parentId));
        } else {
          node.isExpand = Boolean(ids.includes(node[this.c_options.idKey]));
          node.isHidden = false;
        }
        this.initNode_(node);
      });
      this.setCount_();
    },

    /***
     * 初始化节点所需要的字段
     * @param {object} node 节点
     * */
    initNode_(node) {
      node.checked = node.checked || false;
      node.indeterminate = node.indeterminate || false;
      node[this.c_options.disabledKey] = node[this.c_options.disabledKey] || false;
    },
    setDisabledNodes(nodes = []) {
      this.setDisabledKeys(nodes.map(item => item[this.c_options.idKey]))
    },
    getDisabledKeys() {
      return this.big.disabledList.map(item => item[this.c_options.idKey])
    },
    getDisabledNodes() {
      return this.big.disabledList
    },
    setDisabledKeys(keys = []) {
      if (!Array.isArray(keys)) {
        console.warn('The argument to function setCheckedKeys must be an array');
        return;
      }
      const nodes = keys.map(id => this.big.listMap[id]);
      this.big.list.forEach(item => {
         item[this.c_options.disabledKey] = false
      })
      nodes.forEach(item => {
        item[this.c_options.disabledKey] = true
      })
      this.big.disabledList = nodes
    },
    /***
     * 回显选中状态
     * @param {string[]} keys id的列表
     * */
    setCheckedKeys(keys = []) {
      if (!Array.isArray(keys)) {
        console.warn('The argument to function setCheckedKeys must be an array');
        return;
      }
      this.clearChecked();
      const nodes = keys.map(id => this.big.listMap[id]);
      const parentIds = new Set()
      nodes.forEach(node => {
        // debugger
        if (node) {
          node.checked = true;
          // 如果强关联
          if (!this.checkStrictly) {
            // 则改变其相关项
            // 先将所有node 根据parentId 分组
            parentIds.add(getPid(node))
            this.handleCheckedChange_(node, false)
          }
        }
      });
      // debugger
      this.handleParentCheckedChange_(parentIds, this.c_options)
      this.emitChecked_();
    },
    /**
     * 更改所有父级的
     * */
    handleParentCheckedChange_(parentIds, options) {
      const parentIds_ = new Set()
      for (const item of parentIds) {
        const node = this.big.filterMap[item]
        if (!node) continue
        const pid = getPid(node)
        pid && parentIds_.add(pid)
        let checkedLength = 0
        let indeterminate = false
        const childLength = node[options.childrenKey].length
        if (childLength === 0) continue
        node[options.childrenKey].forEach(i => {
          i.checked && checkedLength++
          if (i.indeterminate) {
            indeterminate = true
          }
        })
        node.checked = checkedLength === childLength;
        node.indeterminate = checkedLength !== childLength && checkedLength !== 0 || indeterminate;

      }
      if (parentIds_.size > 0) {
        this.handleParentCheckedChange_(parentIds_, options)
      }
    },
    /***
     * 回显选中状态
     * @param {object[]} nodes node的列表
     * */
    setCheckedNodes(nodes = []) {
      if (!Array.isArray(nodes)) {
        console.warn('The argument to function setCheckedNodes must be an array');
        return;
      }
      if (nodes.length > 0) {
        const keys = nodes.map(i => i[this.c_options.idKey]);
        this.setCheckedKeys(keys);
      } else {
        this.setCheckedKeys([]);
      }
    },

    /***
     * 获取选中状态
     * @returns {string[]}
     * */
    getCheckedKeys() {
      return this.big.checkedKeys;
    },
    /**
     * 获取半选中状态
     * @returns {string[]}
     * */
    getIndeterminateKeys() {
      return this.big.indeterminateKeys
    },
    /**
     * 获取半选中状态
     * @returns {object[]}
     * */
    getIndeterminateNodes() {
      return this.big.indeterminateNodes
    },
    /***
     * 获取选中状态
     * @returns {object[]}
     * */
    getCheckedNodes() {
      return this.big.checkedNodes;
    },
    /***
     * 点击展开与收缩
     * @param {object} node 节点
     * */
    onExpand_(node) {
      node.isExpand = !node.isExpand;
      this.showOrHiddenChildren_(node, !node.isExpand);
    },
    /***
     * 点击checkbox
     * @param {object} node 节点
     * */
    onChecked_(node) {
      this.handleCheckedChange_(node);
      this.emitChecked_();
      this.$emit('onClickCheckbox', node);
    },

    /***
     * 发送给父组件选中信息
     * */
    emitChecked_() {
      this.big.checkedNodes = this.big.list.filter(i => i.checked); // 返回”所有“选中的节点
      this.big.indeterminateNodes = this.big.list.filter(i => i.indeterminate); // 返回”所有“半选中节点

      this.big.checkedKeys = this.big.checkedNodes.map(i => i[this.c_options.idKey]);
      this.big.indeterminateKeys = this.big.indeterminateNodes.map(i => i[this.c_options.idKey]);
      this.$emit('onChange', { checkedKeys: this.big.checkedKeys, checkedNodes: this.big.checkedNodes,indeterminateKeys: this.big.indeterminateKeys, indeterminateNodes: this.big.indeterminateNodes });
      this.setCount_();
    },

    /***
     * 处理选中逻辑
     * @param {object} node 节点
     * @param checkParent 是否直接更新父级
     * */
    handleCheckedChange_(node, checkParent = true) {
      // 父子不互相关联
      if (this.checkStrictly) {
        node.indeterminate = node.isLeaf ? false : node.checked;
        return;
      }
      // 父子强关联
      if (node.checked) node.indeterminate = false;
      this.doChildrenChecked_(node);
      if (checkParent) {
        const parentNode = getPid(node)
        parentNode && this.handleParentCheckedChange_([parentNode], this.c_options)
      }
    },

    /***
     * 1. 隐藏： 子孙后代都要隐藏， 2. 展开：仅儿子展开, value
     * @param {object} node 节点
     * @param {boolean} isHidden 是否隐藏
     * */
    showOrHiddenChildren_(node, isHidden) {
      if (node.isLeaf) return;
      if (isHidden) {
        depthFirstEach({ tree: node[this.c_options.childrenKey], options: this.c_options }, node => {
          node.isHidden = isHidden;
          node.isExpand = false;
        });
      } else {
        node[this.c_options.childrenKey].forEach(j => {
          j.isHidden = isHidden;
          j.isExpand = false;
        });
      }
      this.setCount_();
    },


    /***
     *  处理子、孙后代
     * @param {object} node 节点
     * */
    doChildrenChecked_(node) {
      // debugger
      if (!node[this.c_options.childrenKey] || node[this.c_options.childrenKey].length === 0) return;
      const checked = node.checked;
      depthFirstEach({ tree: node[this.c_options.childrenKey], options: this.c_options }, i => {
        if (i.isLeaf && i[this.c_options.disabledKey]) return
        if (!i[this.c_options.disabledKey]) {
          i.indeterminate = false;
          i.checked = checked;
        }

      });
    },
    /***
     * 监听滚动
     * */
    onScroll_() {
      this.throttleScroll();
    },
    // 设置可见区域的区间
    setRenderRange_(scrollTop = this.$refs['content-wrap'].scrollTop) {
      const clientHeight = this.$refs['content-wrap'].clientHeight
      const count = Math.ceil(clientHeight / this.itemHeight) + this.cacheCount; // 可见项数

      const end = scrollTop + clientHeight >= this.endIndex * this.itemHeight
      const start = scrollTop <= this.startIndex * this.itemHeight

      if (end || start) {
        // 重新计算
        const startIndex = Math.floor(scrollTop / this.itemHeight);
        this.startIndex = startIndex > 20 ? startIndex - 20 : 0;
        this.endIndex = this.startIndex + count;
        this.setCount_();
      }
    },


    /***
     * 筛选节点
     * @param {string} op 类型
     * */
    initFilter_(op) {
      // set this.big.filterList
      this.setFilterList_(op);
      this.setCount_();
      // 过滤后的tree  同时也将children挂载到了this.filterList的节点
      this.big.filterTree = listToTree(this.big.filterList, this.c_options);
      // 如果需要过滤数据的,则数量在此处计算
      if (this.c_countOptions.filter) {
        this.computedCount_()
        const count = this.getCount()
        this.$emit('onFilter', { count})
      }
      this.big.disabledList = this.big.filterList.filter(i => i[this.c_options.disabledKey]);
    },
    /**
     * 计算数量
     * */
    computedCount_() {
      // 如果需要计算 或者 显示(显示必然需要计算),则计算之
      if (this.c_countOptions.computed || this.c_countOptions.showCount) {
        const extend = this.c_countOptions.extendCount || []
        const list = this.c_countOptions.filter ? this.big.filterList : this.big.list
        const map = this.c_countOptions.filter ? this.big.filterMap : this.big.listMap
        list.forEach(item => {
          this.setSelfAndParentCount_({item, map, extend})
        })
      }
    },
    setSelfAndParentCount_({item, map, extend}) {
      const path = item.path
      const hasKey = {}
      for (let i = path.length - 1; i >= 0; i--) {
        const item_ = map[path[i]]
        if (item_.leafCount_ == null) {
          item_.leafCount_ = 0
        } else {
          item_.leafCount_++
        }
        extend.forEach(k => {
          if (item_[k.key + 'Count_'] == null) item_[k.key + 'Count_'] = 0
          if (hasKey[k.key]) {
            // 如果hasKey中存在 ,说明子级存在, 则直接+1
            item_[k.key + 'Count_']++
          } else {
            // 如果不存在 ,且是最后一层级则判断是否有该key
            // eslint-disable-next-line
            if (i === (path.length - 1) && item_.hasOwnProperty(k.key) && item_[k.key] === k.value) {
              item_[k.key + 'Count_']++
              hasKey[k.key] = true
            }
          }
        })
      }
    },
    /***
     * 设置filterList 的map
     * @param {object} options 选项参数
     * */
    setFilterMap_(options) {
      this.big.filterMap = {}
      this.big.filterList.forEach(item => {
        this.big.filterMap[item[options.idKey]] = item
      })
    },
    /***
     * 设置filterList
     * @param {string} op 参数
     * */
    setFilterList_(op) {
      if (op === 'showCheckedOnly') {
        // 不直接 this.big.filterList = this.big.checkedNodes, 因为之前的 filter 将 滤掉的 非叶子节点indeterminate = true 丢失了。场景，1. 输入关键字，2. 点击showCheckedOnly
        this.big.filterList = checkedOnlyFilter(this.big.list, this.c_options)
        this.setFilterMap_(this.c_options)
        return;
      }
      if (this.isOnlyInCheckedSearch && this.big.allCheckedList.length > 0) {
        if (this.keyword.trim() === '') {
          this.big.filterList = this.big.allCheckedList;
          this.setFilterMap_(this.c_options)
          return;
        }
        this.big.filterList = keywordFilter(this.keyword, this.big.allCheckedList, this.c_options)
        this.setFilterMap_(this.c_options)
        return;
      }
      let list = this.big.list
      if (this.extendFilter) {
        // 如果有扩展搜索
        list = extentFilter(this.extendFilter, list, this.big.listMap, this.c_options)
      }
      if (this.keyword.trim() === '') {
        this.big.filterList = list;
        this.setFilterMap_(this.c_options)
        return;
      }
      this.big.filterList = keywordFilter(this.keyword, list, this.c_options)
      this.setFilterMap_(this.c_options)
    },

    /***
     * 回到顶部
     * */
    backToTop() {
      this.$nextTick(() => {
        this.$refs['content-wrap'].scrollTop = 0;
        this.setRenderRange_();
      });
    },
    /***
     * 清空所有选中
     * */
    clearChecked() {
      this.big.list.forEach(node => {
        node.checked = false;
        node.indeterminate = false;
      });
    },

    /***
     * 根据 count 触发 computed
     * */
    setCount_() {
      this.count++;
    },

    /***
     * 仅展示选中的项
     * @param {boolean} isOnlyInCheckedSearch 仅显示选中的搜索 - 默认true
     * */
    showCheckedOnly(isOnlyInCheckedSearch = true) {
      this.keyword = '';
      this.init_('showCheckedOnly');
      // 开关，仅在选中节点里筛选
      this.isOnlyInCheckedSearch = isOnlyInCheckedSearch;
      if (isOnlyInCheckedSearch) {
        this.big.allCheckedList = this.big.checkedNodes.slice();
      } else {
        this.big.allCheckedList = [];
      }
    },
    /**
     * 单击右键
     */
    rightClick_(e, node) {
      this.$emit('onRightClickItem', e, node)
    },
    /**
     * 重置
     */
    restore() {
      this.isOnlyInCheckedSearch = false;
      this.big.allCheckedList = [];
      this.init_('restore');
    },
    /**
     * 手动更新选中状态
     */
    update() {
      this.setCount_();
    },
    /**
     * 清空内存占用
     */
    clear() {
      this.count = 1;
      this.keyword = ''; // 关键词
      this.startIndex = 0; // 渲染的开始区间
      this.endIndex = 70; // 渲染的结束区间
      this.isOnlyInCheckedSearch = false;
      clearAll(this.big.list);
      if (this.big) {
        this.big._data = []; // 海量数据 tree
        this.big.list = []; // 扁平化的tree
        this.big.filterList = []; // 根据关键词过滤后的list
        this.big.filterMap = {}; // 根据关键词过滤后的list 对应的map
        this.big.listMap = {}; // this.big.filterList 对应的 map
        this.big.filterTree = []; // 根据关键词过滤后的tree
        this.big.disabledList = []; // disabled 为true组成的数组
        this.big.checkedKeys = []; // 选中的 ids
        this.big.checkedNodes = []; // 选中的 nodes
        this.big.indeterminateNodes = []; // 半选中的 nodes
        this.big.indeterminateKeys = []; // 半选中的 nodes
      }
    },
  },
};
</script>
<style lang="scss">
  $border-color: #409eff;
.qhh-virtual-tree {
  min-height: 50px;
  height: 100%;
  display: flex;
  flex-direction: column;
  .search-bar {
    padding: 0 10px 10px 10px;
    display: flex;
    .input {
      flex: 1;
      position: relative;
      .filter-input {
        width: 100%;
        box-sizing: border-box;
        border: 1px solid #dcdfe6;
        border-right: none;
        padding: 8px 10px;
        &:focus {
          outline: none;
          border-color: $border-color;
        }
      }
      .clear-input {
        position: absolute;
        border-radius: 50%;
        font-style: normal;
        width: 12px;
        top: 10px;
        height: 12px;
        right: 6px;
        border: 1px solid #cccccc;
        color: #ccc;
        display: inline-block;
        cursor: pointer;
        &::after {
          content: '\00D7';
          position: absolute;
          top: -4px;
          left: 1px;
          transform: scale(0.7);
        }
        &:hover {
          border-color: $border-color;
          color: $border-color;
        }
      }
    }
    .search-btn {
      width: 50px;
      background-color: $border-color;
      color: #ffffff;
      border: none;
      cursor: pointer;
      &:hover {
        background-color: #228af1;
      }
    }
  }
  .content-wrap {
    position: relative;
    overflow: auto;
    padding: 0 10px;
    flex: 1;
    .tree-phantom {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      z-index: -1;
    }
    .tree-content {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      .item-section {
        padding: 0 12px 0 4px;
        &:hover {
          background-color: #f9fafc;
        }
        &.is-hidden {
          display: none;
        }
      }
      .item {
        position: relative;
        display: flex;
        align-items: center;
        //padding: 0 12px 0 4px;
        height: 100%;
        width: 100%;
        > div {
          position: relative;
          display: flex;
          align-items: center;
          height: 100%;
          width: 100%;
        }
        .expand-icon-box {
          cursor: pointer;
          display: flex;
          padding: 0 8px;
          height: 100%;
          justify-content: center;
          align-items: center;
        }
        .custom-expand-node {
          box-sizing: border-box;
          position: relative;
          transition: transform 0.3s ease-in-out;
          &.is-expand {
            transform: rotate(90deg);
          }
          &:hover {
            color: $border-color;
            border-color: $border-color;
            background-color: #f1f5f8;
          }
        }
        .expand-node-border {
          border: 1px solid #4a4a4a;
        }
        .expand-node {
          box-sizing: border-box;
          position: relative;
          transition: transform 0.3s ease-in-out;
          cursor: pointer;
          & .expand-icon {
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
            background-repeat: no-repeat;
            background-position: center;
          }
          &.is-expand {
            transform: rotate(180deg);
          }
          &:hover {
            color: $border-color;
            border-color: $border-color;
            background-color: #f1f5f8;
          }

        }
        .is-disabled {
          cursor: not-allowed;
          pointer-events: none;
          .label {
            color: #aaaaaa;
          }
        }
      }
    }
  }
  .no-data {
    text-align: center;
  }
}
</style>
